﻿
//	------------------------------------------------------------------------------
//                                       _                          
//              _ __ ___   ___ _ __ ___ | |__  _ __ __ _ _ __   ___ 
//             | '_ ` _ \ / _ \ '_ ` _ \| '_ \| '__/ _` | '_ \ / _ \
//             | | | | | |  __/ | | | | | |_) | | | (_| | | | |  __/
//             |_| |_| |_|\___|_| |_| |_|_.__/|_|  \__,_|_| |_|\___|
//                                                     
//	------------------------------------------------------------------------------

#region - License -
//
//	------------------------------------------------------------------------------                                                   
//	         Copyright (c) 2010 Peter Vyvey (peter.vyvey@chiliware.be)
//	------------------------------------------------------------------------------
//
//
//	This file is part of CHILIWARE MEMBRANE.
//
//	CHILIWARE MEMBRANE is free software: you can redistribute it and/or modify
//	it under the terms of the GNU Lesser General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	CHILIWARE MEMBRANE is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU Lesser General Public License for more details.
//
//	You should have received a copy of the GNU Lesser General Public License
//	along with CHILIWARE MEMBRANE.  If not, see <http://www.gnu.org/licenses/>.
//
//
#endregion

#region - Changes -

//	peter.vyvey@chiliware.be     2010-08-04      Created.

#endregion

#region - Using -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;

#endregion

namespace Chiliware.Membrane.Extension
{
	#region - CollectionExtension -

	/// <summary>
	/// Extension methods to handle extra functionality on objects
	/// </summary>
	public static class CollectionExtension
	{
		#region - Public static methods -

		/// <summary>
		/// Wrapper extension method around the <see cref="IEnumerable"/> Count extension method.
		/// </summary>
		/// <typeparam name="T">The item type of the <see cref="IEnumerable"/>.</typeparam>
		/// <param name="sequence">The <see cref="IEnumerable"/> object.</param>
		/// <returns>TRUE if the <see cref="IEnumerable"/> contains any items, FALSE otherwise.</returns>
		public static bool HasItems<T>(this IEnumerable<T> sequence)
		{
			return sequence.Count() != 0;
		}

		/// <summary>
		/// Performs the specified action on each element of IEnumerable{T}.
		/// </summary>
		/// <typeparam name="T">The type of the items in IEnumerable{T}.</typeparam>
		/// <param name="sequence">The enumerable sequence.</param>
		/// <param name="action">The System.Action{T} delegate to perform on each element of IEnumerable{T}.</param>
		public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action)
		{
			sequence.CatchNullArgument("sequence");
			action.CatchNullArgument("action");

			foreach (var item in sequence)
			{
				action(item);
			}
		}

		/// <summary>
		/// Converts a <see cref="IList"/> collection to a generic list of type T.
		/// </summary>
		/// <typeparam name="T">The type of list items.</typeparam>
		/// <param name="sequence">The sequence to convert.</param>
		/// <returns>A converted list of T.</returns>
		public static List<T> ToList<T>(this IList sequence)
		{
			List<T> list = default(List<T>);

			if (null != sequence)
			{
				list = new List<T>();
				foreach (var item in sequence)
				{
					list.Add((T)item);
				}
			}

			return list;
		}

		/// <summary>
		/// Tries to get a value out of a collection.
		/// </summary>
		/// <typeparam name="T">The type of the items in the IEnumerable.</typeparam>
		/// <param name="sequence">The IEnumerable of type T.</param>
		/// <param name="predicate">The delegate that filters the items in the IEnumerable.</param>
		/// <param name="value">The parameter that will receive the resulting instance.</param>
		/// <returns>TRUE if filtering with the predecate resulted in one or more objects, FALSE otherwise.</returns>
		public static bool TryGetValue<T>(this IEnumerable<T> sequence, Func<T, bool> predicate, out T value) 
			where T : class
		{
			value = sequence.Where(predicate).SingleOrDefault();
			return default(T) != value;
		}

		/// <summary>
		/// <para>Gets a single item out of a <see cref="IEnumerable{T}"/>.</para>
		/// <para>Throws an exception with an exception message if multiple items meet the predicate condition.</para>
		/// </summary>
		/// <typeparam name="T">The type of items.</typeparam>
		/// <param name="sequence"><see cref="IEnumerable{T}"/></param>
		/// <param name="exceptionMessage">The exception message if multiple items are found.</param>
		/// <returns>An item of type T.</returns>
		/// <exception cref="System.Exception">Thrown if multiple items in the <see cref="IEnumerable{T}"/> sequence would be returned.</exception>
		public static T SingleItem<T>(this IEnumerable<T> sequence, string exceptionMessage)
			where T : class
		{
			return sequence.SingleItem(item => true, exceptionMessage);
		}

		/// <summary>
		/// <para>Gets a single item out of a <see cref="IEnumerable{T}"/>.</para>
		/// <para>Throws an exception with an exception message if multiple items meet the predicate condition.</para>
		/// </summary>
		/// <typeparam name="T">The type of items.</typeparam>
		/// <param name="sequence"><see cref="IEnumerable{T}"/></param>
		/// <param name="predicate">The condition the return item should meet.</param>
		/// <param name="exceptionMessage">The exception message if multiple items are found.</param>
		/// <returns>An item of type T.</returns>
		/// <exception cref="System.Exception">Thrown if multiple items in the <see cref="IEnumerable{T}"/> sequence could be returned.</exception>
		public static T SingleItem<T>(this IEnumerable<T> sequence, Func<T, bool> predicate, string exceptionMessage)
			where T : class
		{
			if (sequence.Count(predicate) > 1)
			{
				throw new Exception(exceptionMessage);
			}

			return sequence.Single(predicate);
		}

		/// <summary>
		/// <para>Gets a single item out of a <see cref="IEnumerable{T}"/> or returns default{T} if the sequence contains no elements.</para>
		/// <para>Throws an exception with an exception message if multiple items meet the predicate condition.</para>
		/// </summary>
		/// <typeparam name="T">The type of items.</typeparam>
		/// <param name="sequence"><see cref="IEnumerable{T}"/></param>
		/// <param name="exceptionMessage">The exception message if multiple items are found.</param>
		/// <returns>An item of type T.</returns>
		/// <exception cref="System.Exception">Thrown if multiple items in the <see cref="IEnumerable{T}"/> sequence would be returned.</exception>
		public static T SingleOrDefaultItem<T>(this IEnumerable<T> sequence, string exceptionMessage) 
			where T : class
		{
			return sequence.SingleOrDefaultItem(item => true, exceptionMessage);
		}

		/// <summary>
		/// <para>Gets a single item out of a <see cref="IEnumerable{T}"/> or returns default{T} if the sequence contains no elements.</para>
		/// <para>Throws an exception with an exception message if multiple items meet the predicate condition.</para>
		/// </summary>
		/// <typeparam name="T">The type of items.</typeparam>
		/// <param name="sequence"><see cref="IEnumerable{T}"/></param>
		/// <param name="predicate">The condition the return item should meet.</param>
		/// <param name="exceptionMessage">The exception message if multiple items are found.</param>
		/// <returns>An item of type T.</returns>
		/// <exception cref="System.Exception">Thrown if multiple items in the <see cref="IEnumerable{T}"/> sequence could be returned.</exception>
		public static T SingleOrDefaultItem<T>(this IEnumerable<T> sequence, Func<T, bool> predicate, string exceptionMessage) 
			where T : class
		{
			if (sequence.Count(predicate) > 1)
			{
				throw new Exception(exceptionMessage);
			}

			return sequence.SingleOrDefault(predicate);
		}

		#endregion
	}

	#endregion
}
